using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace FileCopyUtility
{
    public class DirectoryCopier
    {
        // 线程池大小，可以根据系统性能调整
        private readonly int _maxThreads;
        
        // 缓冲区大小，用于大文件分块读写
        private const int BufferSize = 65536; // 64KB
        
        // 信号量，控制同时运行的线程数
        private SemaphoreSlim _semaphore;
        
        // 统计信息
        private int _filesCopied = 0;
        private long _bytesCopied = 0;
        private readonly object _lockObject = new object();
        
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="maxThreads">最大并发线程数，默认为处理器核心数</param>
        public DirectoryCopier(int maxThreads = 0)
        {
            // 如果未指定线程数，使用处理器核心数
            _maxThreads = maxThreads > 0 ? maxThreads : Environment.ProcessorCount;
            _semaphore = new SemaphoreSlim(_maxThreads, _maxThreads);
        }
        
        /// <summary>
        /// 拷贝整个文件夹
        /// </summary>
        /// <param name="sourceDir">源文件夹路径</param>
        /// <param name="destinationDir">目标文件夹路径</param>
        /// <param name="recursive">是否递归拷贝子文件夹</param>
        public async Task CopyDirectoryAsync(string sourceDir, string destinationDir, bool recursive = true)
        {
            // 检查源文件夹是否存在
            if (!Directory.Exists(sourceDir))
            {
                throw new DirectoryNotFoundException($"源文件夹不存在: {sourceDir}");
            }
            
            // 重置统计信息
            _filesCopied = 0;
            _bytesCopied = 0;
            
            Console.WriteLine($"开始拷贝文件夹: {sourceDir} -> {destinationDir}");
            Console.WriteLine($"使用 {_maxThreads} 个并发线程");
            
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            
            // 创建目标文件夹
            Directory.CreateDirectory(destinationDir);
            
            // 获取所有文件和文件夹
            var files = new List<string>();
            var dirs = new List<string>();
            
            if (recursive)
            {
                // 递归获取所有文件和文件夹
                files.AddRange(Directory.GetFiles(sourceDir, "*", SearchOption.AllDirectories));
                dirs.AddRange(Directory.GetDirectories(sourceDir, "*", SearchOption.AllDirectories));
            }
            else
            {
                // 仅获取当前目录的文件
                files.AddRange(Directory.GetFiles(sourceDir));
            }
            
            // 创建所有子文件夹
            foreach (var dir in dirs)
            {
                string relativePath = dir.Substring(sourceDir.Length);
                string targetDir = Path.Combine(destinationDir, relativePath.TrimStart(Path.DirectorySeparatorChar));
                Directory.CreateDirectory(targetDir);
            }
            
            // 使用任务列表跟踪所有拷贝任务
            var tasks = new List<Task>();
            
            // 并发拷贝所有文件
            foreach (var file in files)
            {
                string relativePath = file.Substring(sourceDir.Length);
                string targetFile = Path.Combine(destinationDir, relativePath.TrimStart(Path.DirectorySeparatorChar));
                
                // 添加拷贝任务
                tasks.Add(CopyFileWithSemaphoreAsync(file, targetFile));
            }
            
            // 等待所有任务完成
            await Task.WhenAll(tasks);
            
            stopwatch.Stop();
            
            Console.WriteLine($"\n拷贝完成!");
            Console.WriteLine($"成功拷贝 {_filesCopied} 个文件，总计 {(double)_bytesCopied / (1024 * 1024):F2} MB");
            Console.WriteLine($"耗时: {stopwatch.Elapsed.TotalSeconds:F2} 秒");
        }
        
        /// <summary>
        /// 使用信号量控制的文件拷贝方法
        /// </summary>
        private async Task CopyFileWithSemaphoreAsync(string sourceFile, string destinationFile)
        {
            // 获取信号量，限制并发数
            await _semaphore.WaitAsync();
            
            try
            {
                await CopyFileAsync(sourceFile, destinationFile);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"拷贝文件失败: {sourceFile} -> {destinationFile}");
                Console.WriteLine($"错误: {ex.Message}");
            }
            finally
            {
                // 释放信号量
                _semaphore.Release();
            }
        }
        
        /// <summary>
        /// 异步拷贝单个文件（支持大文件）
        /// </summary>
        private async Task CopyFileAsync(string sourceFile, string destinationFile)
        {
            try
            {
                FileInfo fileInfo = new FileInfo(sourceFile);
                long fileSize = fileInfo.Length;
                
                // 使用异步文件流进行拷贝，支持大文件
                using (FileStream sourceStream = new FileStream(sourceFile, FileMode.Open, FileAccess.Read, FileShare.Read, BufferSize, true))
                using (FileStream destinationStream = new FileStream(destinationFile, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize, true))
                {
                    // 使用缓冲区进行分块拷贝
                    await sourceStream.CopyToAsync(destinationStream, BufferSize);
                }
                
                // 更新统计信息
                lock (_lockObject)
                {
                    _filesCopied++;
                    _bytesCopied += fileSize;
                    
                    // 每10个文件或大文件拷贝完成后显示进度
                    if (_filesCopied % 10 == 0 || fileSize > 10 * 1024 * 1024) // 10MB以上视为大文件
                    {
                        Console.WriteLine($"已拷贝: {_filesCopied} 个文件, " +
                                          $"总计: {(double)_bytesCopied / (1024 * 1024):F2} MB, " +
                                          $"当前: {Path.GetFileName(sourceFile)} ({(double)fileSize / (1024 * 1024):F2} MB)");
                    }
                }
            }
            catch (Exception ex)
            {
                // 删除可能已创建的不完整文件
                if (File.Exists(destinationFile))
                {
                    try
                    { 
                        File.Delete(destinationFile);
                    }
                    catch { }
                }
                throw new IOException($"拷贝文件失败: {sourceFile}", ex);
            }
        }
        
        /// <summary>
        /// 同步版本的文件夹拷贝方法
        /// </summary>
        public void CopyDirectory(string sourceDir, string destinationDir, bool recursive = true)
        {
            // 调用异步方法并等待完成
            CopyDirectoryAsync(sourceDir, destinationDir, recursive).GetAwaiter().GetResult();
        }
    }
}